17 实践课-智能分拣产线场景建模

- 智能分拣产线场景建模

关联:索引

本次实践目录组织(建议统一,便于排错与提交)

建议在任意工作目录创建一个“可交付”的场景工程目录:

mkdir -p sorting_line_scene/{worlds,models,docs}

逐行解释:

资源路径建议(让 Gazebo 找得到 models/):

export GZ_SIM_RESOURCE_PATH=$GZ_SIM_RESOURCE_PATH:$(pwd)/sorting_line_scene
export IGN_GAZEBO_RESOURCE_PATH=$IGN_GAZEBO_RESOURCE_PATH:$(pwd)/sorting_line_scene

逐行解释:

验证(可截图留证):

echo $GZ_SIM_RESOURCE_PATH

逐行解释:

配套项目对齐(03_sorting_line_scene

如果你使用的是课程提供的配套项目,请直接使用下面目录(已按本结构预置):

03_sorting_line_scene/
  sorting_line_scene/
    worlds/
    models/
    docs/

快速启动(推荐先跑最小闭环,再跑含设备版):

cd 03_sorting_line_scene/sorting_line_scene
export GZ_SIM_RESOURCE_PATH=$GZ_SIM_RESOURCE_PATH:$(pwd)
export IGN_GAZEBO_RESOURCE_PATH=$IGN_GAZEBO_RESOURCE_PATH:$(pwd)

ign gazebo -v 4 worlds/sorting_line_full.world
# 或:包含 UR5e/AGV/分拣箱占位模型的整线骨架
ign gazebo -v 4 worlds/sorting_line_full_with_devices.world

逐行解释:


  1. UR5e:完成模型导入与基础适配(关节限位正确、安装位姿正确、关键坐标系明确)。
  2. 传送带:完成模型创建(几何/碰撞/摩擦)与速度调控(能做到“改一个参数,速度变化可验证”)。
  3. AI:完成 1 次“生成 SDF/URDF 模板并自检”的闭环(生成不等于可用,必须审计与运行验证)。

本不强制你获取模型的来源,但强制你满足“模型目录结构与资源可解析”。

2. 模型目录结构(Gazebo Standalone Model 结构)

把 UR5e 放到下面结构(目录名可用 ur5e):

sorting_line_scene/
  models/
    ur5e/
      model.config
      model.sdf
      meshes/
        ...

解释要点:

一个最小可用 model.config(复制即可用):

<?xml version="1.0"?>
<model>
  <name>ur5e</name>
  <version>1.0</version>
  <sdf version="1.8">model.sdf</sdf>
  <author>
    <name>course</name>
    <email>n/a</email>
  </author>
  <description>UR5e model for sorting line lab</description>
</model>

逐段解释:

<?xml version="1.0"?>
<model>
  <name>ur5e</name>
  <version>0.1</version>
  <sdf version="1.8">model.sdf</sdf>
  <author>
    <name>course</name>
    <email>n/a</email>
  </author>
  <description>UR5e placeholder model for world integration</description>
</model>
<?xml version="1.0"?>
<sdf version="1.8">
  <model name="ur5e">
    <static>true</static>
    <pose>0 0 0 0 0 0</pose>
    <link name="base_link">
      <inertial>
        <mass>30.0</mass>
        <inertia>
          <ixx>1.0</ixx>
          <iyy>1.0</iyy>
          <izz>1.0</izz>
          <ixy>0</ixy>
          <ixz>0</ixz>
          <iyz>0</iyz>
        </inertia>
      </inertial>
      <collision name="base_collision">
        <geometry>
          <cylinder>
            <radius>0.12</radius>
            <length>0.18</length>
          </cylinder>
        </geometry>
      </collision>
      <visual name="base_visual">
        <geometry>
          <cylinder>
            <radius>0.12</radius>
            <length>0.18</length>
          </cylinder>
        </geometry>
      </visual>
    </link>
  </model>
</sdf>

逐段解释(为什么占位模型也要写惯性与碰撞体):

3. UR5e 在 world 中摆放:安装位姿与坐标系对齐

sorting_line_scene/worlds/sorting_line_full.world(后续会创建)里引用 UR5e:

<include>
  <uri>model://ur5e</uri>
  <name>ur5e_01</name>
  <pose>0.0 -0.6 0.0 0 0 1.5708</pose>
</include>

逐段解释:

4. 关节限位适配:只改“必要字段”,并能自证正确

如果你的 UR5e 模型来自 URDF(或你需要修正关节限位),你要会定位并修改每个关节的 <limit> 字段。

示例(URDF 片段):一个转动关节(revolute)限位写法

<joint name="shoulder_pan_joint" type="revolute">
  <parent link="base_link"/>
  <child link="shoulder_link"/>
  <origin xyz="0 0 0.089159" rpy="0 0 0"/>
  <axis xyz="0 0 1"/>
  <limit lower="-6.283185" upper="6.283185" effort="150.0" velocity="3.15"/>
</joint>

逐段解释:

  1. GUI 自检:在 Gazebo 中逐个拖动关节(或用关节控制面板),观察是否会越过限位或出现“反向折叠”的异常。
  2. 数据自检:记录一个关节的最小/最大可达角度(截图或记录),对照你写的 lower/upper 是否一致。

5. 运动学参数校准:用“可复现对比”而不是主观感觉

最小校准流程(建议按此顺序):

  1. 固定世界坐标系:在布局草图上写清 W 的原点与轴向,并在 world 里把 UR5e pose 锁定到草图位置。
  2. 校准基座:优先校准 base_link 到世界的 pose(不先改 URDF 内部骨架)。
  3. 再校准骨架:只在确有证据时改 <origin xyz rpy>,每次改动后立刻验证(小步快跑)。

1. 传送带模型:先做“稳定可运送”的简化结构

sorting_line_scene/models/conveyor_basic/ 创建模型结构:

sorting_line_scene/
  models/
    conveyor_basic/
      model.config
      model.sdf

一个最小可用 conveyor_basic/model.config(复制即可用):

<?xml version="1.0"?>
<model>
  <name>conveyor_basic</name>
  <version>1.0</version>
  <sdf version="1.8">model.sdf</sdf>
  <author>
    <name>course</name>
    <email>n/a</email>
  </author>
  <description>Basic conveyor geometry for lab</description>
</model>

逐段解释:

conveyor_basic/model.sdf(可直接运行的最小传送带模型):

<?xml version="1.0"?>
<sdf version="1.8">
  <model name="conveyor_basic">
    <static>true</static>
    <link name="belt_link">
      <collision name="belt_collision">
        <geometry>
          <box><size>2.0 0.5 0.1</size></box>
        </geometry>
        <surface>
          <friction>
            <ode>
              <mu>0.9</mu>
              <mu2>0.9</mu2>
            </ode>
          </friction>
        </surface>
      </collision>
      <visual name="belt_visual">
        <geometry>
          <box><size>2.0 0.5 0.1</size></box>
        </geometry>
      </visual>
    </link>
  </model>
</sdf>

逐段解释:

在后续的 world 中,我们将把 belt_speed 写成一个明确常量,并要求你们记录:修改前后速度与现象差异。

AI 辅助提醒(重要):

  1. 快问快答:UR5e 的 pose 里 yaw 用的是什么单位?关节限位 lower/upper 用的是什么单位?传送带碰撞体为什么不推荐直接用高精网格?

2. AGV 模型导入:目录结构与 world 引用

建议把 AGV 模型放到:

sorting_line_scene/
  models/
    agv_basic/
      model.config
      model.sdf
      meshes/
        ...
<?xml version="1.0"?>
<model>
  <name>agv_basic</name>
  <version>0.1</version>
  <sdf version="1.8">model.sdf</sdf>
  <author>
    <name>course</name>
    <email>n/a</email>
  </author>
  <description>AGV placeholder model for world integration</description>
</model>
<?xml version="1.0"?>
<sdf version="1.8">
  <model name="agv_basic">
    <static>true</static>
    <pose>0 0 0 0 0 0</pose>
    <link name="base_link">
      <inertial>
        <mass>80.0</mass>
        <inertia>
          <ixx>2.0</ixx>
          <iyy>6.0</iyy>
          <izz>7.0</izz>
          <ixy>0</ixy>
          <ixz>0</ixz>
          <iyz>0</iyz>
        </inertia>
      </inertial>
      <collision name="chassis_collision">
        <geometry>
          <box><size>0.8 0.5 0.25</size></box>
        </geometry>
      </collision>
      <visual name="chassis_visual">
        <geometry>
          <box><size>0.8 0.5 0.25</size></box>
        </geometry>
      </visual>
    </link>
  </model>
</sdf>

逐段解释:

在 world 中引用(示例):

<include>
  <uri>model://agv_basic</uri>
  <name>agv_01</name>
  <pose>-1.5 0.8 0.0 0 0 0</pose>
</include>

逐段解释:

3. 航路点路径(保底实现):用“可复现”的 waypoints 描述

P1 (-1.5, 0.8) → P2 (1.5, 0.8) → P3 (1.5, -0.8) → P4 (-1.5, -0.8) → P1

解释要点:

1. 为什么苹果的“碰撞体”要简化

苹果网格通常比较复杂。直接用高精网格做碰撞体常见后果:

2. 苹果模型目录结构与最小 model.config

占位版(推荐先跑通,配套项目默认采用该结构):

sorting_line_scene/
  models/
    apple_mesh/
      model.config
      model.sdf

网格版(进阶,可在占位版稳定后再替换为网格视觉):

sorting_line_scene/
  models/
    apple_mesh/
      model.config
      model.sdf
      meshes/
        apple.stl

最小 model.config(复制即可用):

<?xml version="1.0"?>
<model>
  <name>apple_mesh</name>
  <version>1.0</version>
  <sdf version="1.8">model.sdf</sdf>
  <author>
    <name>course</name>
    <email>n/a</email>
  </author>
  <description>Apple mesh with simplified collision</description>
</model>

配套项目(03_sorting_line_scene)默认的 model.config(与占位版 model.sdf 对齐):

<?xml version="1.0"?>
<model>
  <name>apple_mesh</name>
  <version>1.0</version>
  <sdf version="1.8">model.sdf</sdf>
  <author>
    <name>course</name>
    <email>n/a</email>
  </author>
  <description>Apple model placeholder with stable simplified collision</description>
</model>

3. 苹果 model.sdf:占位版(配套项目默认,确保稳定可跑)

apple_mesh/model.sdf(可直接引用到 world):

<?xml version="1.0"?>
<sdf version="1.8">
  <model name="apple_mesh">
    <static>false</static>
    <link name="link">
      <inertial>
        <mass>0.18</mass>
        <inertia>
          <ixx>0.00018</ixx>
          <iyy>0.00018</iyy>
          <izz>0.00018</izz>
          <ixy>0</ixy>
          <ixz>0</ixz>
          <iyz>0</iyz>
        </inertia>
      </inertial>

      <collision name="collision">
        <geometry>
          <sphere><radius>0.045</radius></sphere>
        </geometry>
        <surface>
          <friction>
            <ode>
              <mu>0.6</mu>
              <mu2>0.6</mu2>
            </ode>
          </friction>
        </surface>
      </collision>

      <visual name="visual">
        <geometry>
          <sphere><radius>0.045</radius></sphere>
        </geometry>
      </visual>
    </link>
  </model>
</sdf>

逐段解释(你必须能讲清“为什么这么配”):

补充说明(为什么配套项目先用占位版):

网格版(进阶:视觉用网格,碰撞仍用球体;当你已经准备好 meshes/apple.stl 时再采用):

<visual name="visual">
  <geometry>
    <mesh>
      <uri>model://apple_mesh/meshes/apple.stl</uri>
    </mesh>
  </geometry>
</visual>

逐段解释:

1. 创建整线 world 文件(建议名:sorting_line_full.world)

cd sorting_line_scene
mkdir -p worlds

逐行解释:

worlds/sorting_line_full.world(骨架示例,按组号修改 pose 与实例名即可运行):

<?xml version="1.0" ?>
<sdf version="1.8">
  <world name="sorting_line_full_world">
    <physics name="fast_stable" type="ode">
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1.0</real_time_factor>
      <real_time_update_rate>1000</real_time_update_rate>
    </physics>

    <gravity>0 0 -9.8</gravity>

    <include>
      <uri>https://fuel.gazebosim.org/1.0/OpenRobotics/models/Ground%20Plane</uri>
    </include>
    <include>
      <uri>https://fuel.gazebosim.org/1.0/OpenRobotics/models/Sun</uri>
    </include>

    <include>
      <uri>model://conveyor_basic</uri>
      <name>conveyor_01</name>
      <pose>0 0 0 0 0 0</pose>
    </include>

    <include>
      <uri>model://ur5e</uri>
      <name>ur5e_01</name>
      <pose>0.0 -0.6 0.0 0 0 1.5708</pose>
    </include>

    <include>
      <uri>model://agv_basic</uri>
      <name>agv_01</name>
      <pose>-1.5 0.8 0.0 0 0 0</pose>
    </include>

    <include>
      <uri>model://apple_mesh</uri>
      <name>apple_01</name>
      <pose>-0.9 0.0 0.25 0 0 0</pose>
    </include>
  </world>
</sdf>

逐段解释(只解释你必须会用到的关键点):

1.0 配套项目中的 world 文件(与本对齐的可运行版本)

配套项目提供两个可直接运行的 world,分别对应“最小闭环”和“整线骨架”。以下代码与项目文件保持一致:

worlds/sorting_line_full.world(最小闭环:传送带 + 物料球体,先验证稳定性):

<?xml version="1.0" ?>
<sdf version="1.8">
  <world name="sorting_line_full_world">
    <physics name="fast_stable" type="ode">
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1.0</real_time_factor>
      <real_time_update_rate>1000</real_time_update_rate>
    </physics>

    <gravity>0 0 -9.8</gravity>

    <include>
      <uri>https://fuel.gazebosim.org/1.0/OpenRobotics/models/Ground%20Plane</uri>
    </include>
    <include>
      <uri>https://fuel.gazebosim.org/1.0/OpenRobotics/models/Sun</uri>
    </include>

    <include>
      <uri>model://conveyor_basic</uri>
      <name>conveyor_01</name>
      <pose>0 0 0 0 0 0</pose>
    </include>

    <model name="apple_sphere_test">
      <static>false</static>
      <pose>-0.9 0.0 0.25 0 0 0</pose>
      <link name="link">
        <inertial>
          <mass>0.18</mass>
          <inertia>
            <ixx>0.00018</ixx>
            <iyy>0.00018</iyy>
            <izz>0.00018</izz>
            <ixy>0</ixy>
            <ixz>0</ixz>
            <iyz>0</iyz>
          </inertia>
        </inertial>
        <collision name="collision">
          <geometry>
            <sphere><radius>0.045</radius></sphere>
          </geometry>
          <surface>
            <friction>
              <ode>
                <mu>0.6</mu>
                <mu2>0.6</mu2>
              </ode>
            </friction>
          </surface>
        </collision>
        <visual name="visual">
          <geometry>
            <sphere><radius>0.045</radius></sphere>
          </geometry>
        </visual>
      </link>
    </model>
  </world>
</sdf>

逐段解释:

worlds/sorting_line_full_with_devices.world(整线骨架:含 UR5e/AGV/分拣箱占位模型 + 苹果占位模型):

<?xml version="1.0" ?>
<sdf version="1.8">
  <world name="sorting_line_full_world">
    <physics name="fast_stable" type="ode">
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1.0</real_time_factor>
      <real_time_update_rate>1000</real_time_update_rate>
    </physics>

    <gravity>0 0 -9.8</gravity>

    <include>
      <uri>https://fuel.gazebosim.org/1.0/OpenRobotics/models/Ground%20Plane</uri>
    </include>
    <include>
      <uri>https://fuel.gazebosim.org/1.0/OpenRobotics/models/Sun</uri>
    </include>

    <include>
      <uri>model://conveyor_basic</uri>
      <name>conveyor_01</name>
      <pose>0 0 0 0 0 0</pose>
    </include>

    <include>
      <uri>model://ur5e</uri>
      <name>ur5e_01</name>
      <pose>0.0 -0.6 0.0 0 0 1.5708</pose>
    </include>

    <include>
      <uri>model://agv_basic</uri>
      <name>agv_01</name>
      <pose>-1.5 0.8 0.0 0 0 0</pose>
    </include>

    <include>
      <uri>model://apple_mesh</uri>
      <name>apple_01</name>
      <pose>-0.9 0.0 0.25 0 0 0</pose>
    </include>

    <model name="bin_grade_a">
      <static>true</static>
      <pose>1.2 0.8 0.2 0 0 0</pose>
      <link name="bin_link">
        <collision name="bin_collision">
          <geometry>
            <box><size>0.4 0.4 0.4</size></box>
          </geometry>
        </collision>
        <visual name="bin_visual">
          <geometry>
            <box><size>0.4 0.4 0.4</size></box>
          </geometry>
        </visual>
      </link>
    </model>
  </world>
</sdf>

逐段解释:

做法 A(推荐):把速度控制系统插件加到 apple_mesh/model.sdf

apple_mesh/model.sdf<model> 内、<link> 之前插入:

<plugin
  filename="ignition-gazebo-velocity-control-system"
  name="gz::sim::systems::VelocityControl">
  <initial_linear>0.7 0 0</initial_linear>
  <initial_angular>0 0 0</initial_angular>
</plugin>

逐段解释:

重要提醒(避免把“保底做法”误当成“工业实现”):

做法 B(备用):先用简化球体做“速度调控验证”,再替换为网格苹果

<model name="apple_sphere_test">
  <static>false</static>
  <pose>-0.9 0.0 0.25 0 0 0</pose>
  <plugin
    filename="ignition-gazebo-velocity-control-system"
    name="gz::sim::systems::VelocityControl">
    <initial_linear>0.7 0 0</initial_linear>
    <initial_angular>0 0 0</initial_angular>
  </plugin>
  <link name="link">
    <inertial><mass>0.18</mass></inertial>
    <collision name="collision">
      <geometry><sphere><radius>0.045</radius></sphere></geometry>
    </collision>
    <visual name="visual">
      <geometry><sphere><radius>0.045</radius></sphere></geometry>
    </visual>
  </link>
</model>

逐段解释:

运行(可截图留证):

ign gazebo -v 4 worlds/sorting_line_full.world

逐行解释:

本章 AI 使用的统一要求:带约束提问 + 小步验证 + 证据链闭环(提示词—输出—你采用的修改—运行验证)。

1. 生成设备 SDF/URDF 模板(可直接复制的提示词)

你要检查的点(人工审计清单):

2. 物理参数优化(可直接复制的提示词)

我在 Gazebo Fortress 中做分拣产线仿真。现象:(粘贴现象:打滑/抖动/弹飞/穿透/掉帧)。当前参数如下:(粘贴 physics、质量、摩擦、碰撞体形状)。请给出:1)最可能的 3 个原因(按概率排序);2)每个原因对应的最小改动方案(一次只改 1-2 个参数);3)每个改动的预期现象;4)如何验证(给出可截图的证据点)。

3. 模型加载报错排查(可直接复制的提示词)

  1. 最小闭环运行证据:运行 worlds/sorting_line_full.world,提交 1 张“终端无解析错误”截图 + 1 张“GUI 场景”截图。

  2. 整线骨架运行证据:运行 worlds/sorting_line_full_with_devices.world,提交 1 张“Entity Tree 关键实体可见”截图(conveyor_01/ur5e_01/agv_01/apple_01/bin_grade_a)。

  3. 参数对比证据:提交 1 组“参数修改前/后 + 现象差异”的对比记录(截图 + 1 句话解释),参数可选:摩擦(mu/mu2)、质量(mass)、碰撞半径(radius)。

  4. AI 协同证据:提交 1 次 AI 交互记录(提示词 + 关键输出 + 你采用的最小改动点 + 运行验证证据)。

  5. 生成 1 个设备的建模模板(SDF/URDF 均可):至少包含 1 个你们最终实际运行成功的模型文件(可选:传送带/苹果/AGV 占位模型)。

  6. 针对 1 个模型加载/参数报错:AI 分析原因并给出修改方案;你们选择其中 1 个方案落地并验证。

  7. 结合场景需求做 1 组物理参数优化并对比(例如摩擦或质量/惯性),提交“修改前/后 + 现象 + 证据”。

  8. 生成协同仿真测试要点清单,并补充你们场景特有的 1–2 条测试项(选做加分)。

课后作业(布置)

  1. 提交分拣产线仿真场景文件(.world/.sdf/.urdf),附场景布局说明文档(150 字左右)。
  2. 提交各设备协同仿真测试截图(含设备运动状态、仿真界面),记录测试中发现的问题及解决方法。
  3. 提交 AI 生成的建模代码、修改后的代码及优化说明(200 字左右),附完整 AI 交互记录。

Markdown 与代码自检清单(提交前必须过一遍)